Completed
Pull Request — master (#328)
by
unknown
02:17
created

View._onClickDownloadSelected   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 3
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
/**
2
 * Nextcloud - Gallery
3
 *
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Olivier Paroz <[email protected]>
9
 *
10
 * @copyright Olivier Paroz 2017
11
 */
12
/* global Handlebars, Gallery, GalleryImage, Thumbnails */
13
(function ($, _, OC, t, Gallery) {
14
	"use strict";
15
16
	var TEMPLATE_ADDBUTTON = '<a href="#" class="button new"><span class="icon icon-add"></span><span class="hidden-visually">New</span></a>';
0 ignored issues
show
Coding Style introduced by
Line is too long.
Loading history...
17
	var TEMPLATE_DOWNLOADBUTTON = '<a href="#" id="download-selected-button" class="button download hidden"><span class="icon icon-download"></span>' +
0 ignored issues
show
Coding Style introduced by
Line is too long.
Loading history...
18
		'<span class="hidden-visually">Download</span></a>';
19
20
	/**
21
	 * Builds and updates the Gallery view
22
	 *
23
	 * @constructor
24
	 */
25
	var View = function () {
26
		this.element = $('#gallery');
27
		this.loadVisibleRows.loading = false;
28
		this._setupUploader();
29
		this.breadcrumb = new Gallery.Breadcrumb();
30
		this.emptyContentElement = $('#emptycontent');
31
		this.controlsElement = $('#controls');
32
	};
33
34
	View.prototype = {
35
		element: null,
36
		breadcrumb: null,
37
		requestId: -1,
38
		emptyContentElement: null,
39
		controlsElement: null,
40
		/**
41
		 * Map of file id to file data
42
		 * @type Object.<int, Object>
43
		 */
44
		_selectedFiles: {},
45
		/**
46
		 * Summary of selected files.
47
		 * @type OCA.Files.FileSummary
48
		 */
49
		_selectionSummary: null,
50
		/**
51
		 * Initialiation status
52
		 * @type Boolean
53
		 */
54
		_initialized: false,
55
56
		/**
57
		 * Removes all thumbnails from the view
58
		 */
59
		clear: function () {
60
			this.loadVisibleRows.processing = false;
61
			this.loadVisibleRows.loading = null;
62
			// We want to keep all the events
63
			this.element.children().detach();
64
			this.showLoading();
65
		},
66
67
		/**
68
		 * @param {string} path
69
		 * @returns {boolean}
70
		 */
71
		_isValidPath: function(path) {
72
			var sections = path.split('/');
73
			for (var i = 0; i < sections.length; i++) {
74
				if (sections[i] === '..') {
75
					return false;
76
				}
77
			}
78
79
			return path.toLowerCase().indexOf(decodeURI('%0a')) === -1 &&
80
				path.toLowerCase().indexOf(decodeURI('%00')) === -1;
81
		},
82
83
		/**
84
		 * Populates the view if there are images or albums to show
85
		 *
86
		 * @param {string} albumPath
87
		 * @param {string|undefined} errorMessage
88
		 */
89
		init: function (albumPath, errorMessage) {
90
			// Set path to an empty value if not a valid one
91
			if(!this._isValidPath(albumPath)) {
92
				albumPath = '';
93
			}
94
95
			// Only do it when the app is initialised
96
			if (this.requestId === -1) {
97
				this._initButtons();
98
				this._blankUrl();
99
			}
100
			if ($.isEmptyObject(Gallery.imageMap)) {
101
				Gallery.view.showEmptyFolder(albumPath, errorMessage);
102
			} else {
103
				this.viewAlbum(albumPath);
104
			}
105
106
			this._setBackgroundColour();
107
108
			this._initSelection();
109
			this.initialized = true;
110
		},
111
112
		/**
113
		 * Starts the slideshow
114
		 *
115
		 * @param {string} path
116
		 * @param {string} albumPath
117
		 */
118
		startSlideshow: function (path, albumPath) {
119
			var album = Gallery.albumMap[albumPath];
120
			var images = album.images;
121
			var startImage = Gallery.imageMap[path];
122
			Gallery.slideShow(images, startImage, false);
123
		},
124
125
		/**
126
		 * Sets up the controls and starts loading the gallery rows
127
		 *
128
		 * @param {string|null} albumPath
129
		 */
130
		viewAlbum: function (albumPath) {
131
			albumPath = albumPath || '';
132
			if (!Gallery.albumMap[albumPath]) {
133
				return;
134
			}
135
136
			this.clear();
137
138
			if (albumPath !== Gallery.currentAlbum
139
				|| (albumPath === Gallery.currentAlbum &&
140
				Gallery.albumMap[albumPath].etag !== Gallery.currentEtag)) {
141
				Gallery.currentAlbum = albumPath;
142
				Gallery.currentEtag = Gallery.albumMap[albumPath].etag;
143
				this._setupButtons(albumPath);
144
			}
145
146
			Gallery.albumMap[albumPath].viewedItems = 0;
147
			Gallery.albumMap[albumPath].preloadOffset = 0;
148
149
			// Each request has a unique ID, so that we can track which request a row belongs to
150
			this.requestId = Math.random();
151
			Gallery.albumMap[Gallery.currentAlbum].requestId = this.requestId;
152
153
			// Loading rows without blocking the execution of the rest of the script
154
			setTimeout(function () {
155
				this.loadVisibleRows.activeIndex = 0;
156
				this.loadVisibleRows(Gallery.albumMap[Gallery.currentAlbum]);
157
			}.bind(this), 0);
158
		},
159
160
		/**
161
		 * Manages the sorting interface
162
		 *
163
		 * @param {string} sortType name or date
164
		 * @param {string} sortOrder asc or des
165
		 */
166
		sortControlsSetup: function (sortType, sortOrder) {
167
			var reverseSortType = 'date';
168
			if (sortType === 'date') {
169
				reverseSortType = 'name';
170
			}
171
			this._setSortButton(sortType, sortOrder, true);
172
			this._setSortButton(reverseSortType, 'asc', false); // default icon
173
		},
174
175
		/**
176
		 * Loads and displays gallery rows on screen
177
		 *
178
		 * view.loadVisibleRows.loading holds the Promise of a row
179
		 *
180
		 * @param {Album} album
181
		 */
182
		loadVisibleRows: function (album) {
183
			var view = this;
184
			// Wait for the previous request to be completed
185
			if (this.loadVisibleRows.processing) {
186
				return;
187
			}
188
189
			/**
190
			 * At this stage, there is no loading taking place, so we can look for new rows
191
			 */
192
193
			var scroll = $('#content-wrapper').scrollTop() + $(window).scrollTop();
194
			// 2 windows worth of rows is the limit from which we need to start loading new rows.
195
			// As we scroll down, it grows
196
			var targetHeight = ($(window).height() * 2) + scroll;
197
			// We throttle rows in order to try and not generate too many CSS resizing events at
198
			// the same time
199
			var showRows = _.throttle(function (album) {
200
201
				// If we've reached the end of the album, we kill the loader
202
				if (!(album.viewedItems < album.subAlbums.length + album.images.length)) {
0 ignored issues
show
Coding Style introduced by
The usage of ! looks confusing here.

The following shows a case which JSHint considers confusing and its respective non-confusing counterpart:

! (str.indexOf(i) > -1) // Bad
str.indexOf(i) === -1 // Good
Loading history...
203
					view.loadVisibleRows.processing = false;
204
					view.loadVisibleRows.loading = null;
205
					return;
206
				}
207
208
				// Prevents creating rows which are no longer required. I.e when changing album
209
				if (view.requestId !== album.requestId) {
210
					return;
211
				}
212
213
				// We can now safely create a new row
214
				var row = album.getRow($(window).width());
215
				var rowDom = row.getDom();
216
				view.element.append(rowDom);
217
218
				return album.fillNextRow(row).then(function () {
219
					if (album.viewedItems < album.subAlbums.length + album.images.length &&
220
						view.element.height() < targetHeight) {
221
						return showRows(album);
222
					}
223
					// No more rows to load at the moment
224
					view.loadVisibleRows.processing = false;
225
					view.loadVisibleRows.loading = null;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
226
				}, function () {
227
					// Something went wrong, so kill the loader
228
					view.loadVisibleRows.processing = false;
229
					view.loadVisibleRows.loading = null;
230
				});
231
			}, 100);
232
			if (this.element.height() < targetHeight) {
233
				this._showNormal();
234
				this.loadVisibleRows.processing = true;
235
				album.requestId = view.requestId;
236
				this.loadVisibleRows.loading = showRows(album);
237
			}
238
		},
239
240
		/**
241
		 * Shows an empty gallery message
242
		 *
243
		 * @param {string} albumPath
244
		 * @param {string|null} errorMessage
245
		 */
246
		showEmptyFolder: function (albumPath, errorMessage) {
247
			var message = '<div class="icon-gallery"></div>';
248
			var uploadAllowed = true;
249
250
			this.element.children().detach();
251
			this.removeLoading();
252
253
			if (!_.isUndefined(errorMessage) && errorMessage !== null) {
254
				message += '<h2>' + t('gallery',
255
						'Album cannot be shown') + '</h2>';
256
				message += '<p>' + escapeHTML(errorMessage) + '</p>';
0 ignored issues
show
Bug introduced by
escapeHTML does not seem to be defined.
Loading history...
257
				uploadAllowed = false;
258
			} else {
259
				message += '<h2>' + t('gallery',
260
						'No media files found') + '</h2>';
261
				// We can't upload yet on the public side
262
				if (Gallery.token) {
263
					message += '<p>' + t('gallery',
264
							'Upload pictures in the Files app to display them here') + '</p>';
265
				} else {
266
					message += '<p>' + t('gallery',
267
							'Upload new files via drag and drop or by using the [+] button above') +
268
						'</p>';
269
				}
270
			}
271
			this.emptyContentElement.html(message);
272
			this.emptyContentElement.removeClass('hidden');
273
274
			this._hideButtons(uploadAllowed);
275
			Gallery.currentAlbum = albumPath;
276
			var availableWidth = $(window).width() - Gallery.buttonsWidth;
277
			this.breadcrumb.init(albumPath, availableWidth);
278
			Gallery.config.albumDesign = null;
279
		},
280
281
		/**
282
		 * Dims the controls bar when retrieving new content. Matches the effect in Files
283
		 */
284
		dimControls: function () {
285
			// Use the existing mask if its already there
286
			var $mask = this.controlsElement.find('.mask');
287
			if ($mask.exists()) {
288
				return;
289
			}
290
			$mask = $('<div class="mask transparent"></div>');
291
			this.controlsElement.append($mask);
292
			$mask.removeClass('transparent');
293
		},
294
295
		/**
296
		 * Shows the infamous loading spinner
297
		 */
298
		showLoading: function () {
299
			this.emptyContentElement.addClass('hidden');
300
			this.controlsElement.removeClass('hidden');
301
			$('#content').addClass('icon-loading');
302
			this.dimControls();
303
		},
304
305
		/**
306
		 * Removes the spinner in the main area and restore normal visibility of the controls bar
307
		 */
308
		removeLoading: function () {
309
			$('#content').removeClass('icon-loading');
310
			this.controlsElement.find('.mask').remove();
311
		},
312
313
		/**
314
		 * Shows thumbnails
315
		 */
316
		_showNormal: function () {
317
			this.emptyContentElement.addClass('hidden');
318
			this.controlsElement.removeClass('hidden');
319
			this.removeLoading();
320
		},
321
322
		/**
323
		 * Sets up our custom handlers for folder uploading operations
324
		 *
325
		 * @see OC.Upload.init/file_upload_param.done()
326
		 *
327
		 * @private
328
		 */
329
		_setupUploader: function () {
330
			var $uploadEl = $('#file_upload_start');
331
			if (!$uploadEl.exists()) {
332
				return;
333
			}
334
			this._uploader = new OC.Uploader($uploadEl, {
335
				fileList: FileList,
0 ignored issues
show
Bug introduced by
The variable FileList seems to be never declared. If this is a global, consider adding a /** global: FileList */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
336
				dropZone: $('#content')
337
			});
338
			this._uploader.on('add', function (e, data) {
339
				data.targetDir = '/' + Gallery.currentAlbum;
340
			});
341
			this._uploader.on('done', function (e, upload) {
342
				var data = upload.data;
343
344
				// is that the last upload ?
345
				if (data.files[0] === data.originalFiles[data.originalFiles.length - 1]) {
346
					var fileList = data.originalFiles;
347
					//Ask for a refresh of the photowall
348
					Gallery.getFiles(Gallery.currentAlbum).done(function () {
349
						var fileId, path;
350
						// Removes the cached thumbnails of files which have been re-uploaded
351
						_(fileList).each(function (fileName) {
352
							path = Gallery.currentAlbum + '/' + fileName;
353
							if (Gallery.imageMap[path]) {
354
								fileId = Gallery.imageMap[path].fileId;
355
								if (Thumbnails.map[fileId]) {
356
									delete Thumbnails.map[fileId];
357
								}
358
							}
359
						});
360
361
						Gallery.view.init(Gallery.currentAlbum);
362
					});
363
				}
364
			});
365
366
			// Since Nextcloud 9.0
367
			if (OC.Uploader) {
368
				OC.Uploader.prototype._isReceivedSharedFile = function (file) {
369
					var path = file.name;
370
					var sharedWith = false;
371
372
					if (Gallery.currentAlbum !== '' && Gallery.currentAlbum !== '/') {
373
						path = Gallery.currentAlbum + '/' + path;
374
					}
375
					if (Gallery.imageMap[path] && Gallery.imageMap[path].sharedWithUser) {
376
						sharedWith = true;
377
					}
378
379
					return sharedWith;
380
				};
381
			}
382
		},
383
384
		/**
385
		 * Setups selection feature
386
		 * Also resets file actions list to be hidden in case of user navigating
387
		 * back and forth between albums
388
		 *
389
		 * @private
390
		 */
391
		_initSelection: function() {
392
			this._selectedFiles = {};
393
			this._selectionSummary = new OCA.Files.FileSummary();
394
			if (!this.initialized) {
395
				this.element.on('change', '.selectCheckBox', _.bind(this._onClickFileCheckbox, this));
396
			}
397
398
			var downloadSelectedButton = $('#download-selected-button');
399
			if (downloadSelectedButton && !downloadSelectedButton.hasClass('hidden')) {
400
				downloadSelectedButton.addClass('hidden');
401
			}
402
		},
403
404
		/**
405
		 * Adds all the click handlers to buttons the first time they appear in the interface
406
		 *
407
		 * @private
408
		 */
409
		_initButtons: function () {
410
			this.element.on("contextmenu", function(e) { e.preventDefault(); });
411
			$('#filelist-button').click(Gallery.switchToFilesView);
412
			$('#download').click(Gallery.download);
413
			$('#shared-button').click(Gallery.share);
414
			Gallery.infoBox = new Gallery.InfoBox();
415
			$('#album-info-button').click(Gallery.showInfo);
416
			$('#sort-name-button').click(Gallery.sorter);
417
			$('#sort-date-button').click(Gallery.sorter);
418
			$('.save-form').submit(Gallery.saveForm);
419
			this._renderDownloadSelectedButton();
420
			this._renderNewButton();
421
			// Trigger cancelling of file upload
422
			$('#uploadprogresswrapper .stop').on('click', function () {
423
				OC.Upload.cancelUploads();
424
			});
425
			this.requestId = Math.random();
426
		},
427
428
		/**
429
		 * Sets up all the buttons of the interface and the breadcrumbs
430
		 *
431
		 * @param {string} albumPath
432
		 * @private
433
		 */
434
		_setupButtons: function (albumPath) {
435
			this._shareButtonSetup(albumPath);
436
			this._infoButtonSetup();
437
438
			var availableWidth = $(window).width() - Gallery.buttonsWidth;
439
			this.breadcrumb.init(albumPath, availableWidth);
440
			var album = Gallery.albumMap[albumPath];
441
442
			var sum = album.images.length + album.subAlbums.length;
443
			//If sum of the number of images and subalbums exceeds 1 then show the buttons.
444
			if(sum > 1)
445
			{
446
				$('#sort-name-button').show();
447
				$('#sort-date-button').show();
448
			}
449
			else
450
			{
451
				$('#sort-name-button').hide();
452
				$('#sort-date-button').hide();
453
			}
454
			var currentSort = Gallery.config.albumSorting;
455
			this.sortControlsSetup(currentSort.type, currentSort.order);
456
			Gallery.albumMap[Gallery.currentAlbum].images.sort(
457
				Gallery.utility.sortBy(currentSort.type,
458
					currentSort.order));
459
			Gallery.albumMap[Gallery.currentAlbum].subAlbums.sort(Gallery.utility.sortBy('name',
460
				currentSort.albumOrder));
461
462
			$('#save-button').show();
463
			$('#download').show();
464
			$('a.button.new').show();
465
		},
466
467
		/**
468
		 * Hide buttons in the controls bar
469
		 *
470
		 * @param uploadAllowed
471
		 */
472
		_hideButtons: function (uploadAllowed) {
473
			$('#album-info-button').hide();
474
			$('#shared-button').hide();
475
			$('#sort-name-button').hide();
476
			$('#sort-date-button').hide();
477
			$('#save-button').hide();
478
			$('#download').hide();
479
480
			if (!uploadAllowed) {
481
				$('a.button.new').hide();
482
			}
483
		},
484
485
		/**
486
		 * Shows or hides the share button depending on if we're in a public gallery or not
487
		 *
488
		 * @param {string} albumPath
489
		 * @private
490
		 */
491
		_shareButtonSetup: function (albumPath) {
492
			var shareButton = $('#shared-button');
493
			if (albumPath === '' || Gallery.token) {
494
				shareButton.hide();
495
			} else {
496
				shareButton.show();
497
			}
498
		},
499
500
		/**
501
		 * Shows or hides the info button based on the information we've received from the server
502
		 *
503
		 * @private
504
		 */
505
		_infoButtonSetup: function () {
506
			var infoButton = $('#album-info-button');
507
			infoButton.find('span').hide();
508
			var infoContentContainer = $('.album-info-container');
509
			infoContentContainer.slideUp();
510
			infoContentContainer.css('max-height',
511
				$(window).height() - Gallery.browserToolbarHeight);
512
			var albumInfo = Gallery.config.albumInfo;
513
			if (Gallery.config.albumError) {
514
				infoButton.hide();
515
				var text = '<strong>' + t('gallery', 'Configuration error') + '</strong></br>' +
516
					Gallery.config.albumError.message + '</br></br>';
517
				Gallery.utility.showHtmlNotification(text, 7);
518
			} else if ($.isEmptyObject(albumInfo)) {
519
				infoButton.hide();
520
			} else {
521
				infoButton.show();
522
				if (albumInfo.inherit !== 'yes' || albumInfo.level === 0) {
523
					infoButton.find('span').delay(1000).slideDown();
524
				}
525
			}
526
		},
527
528
		/**
529
		 * Sets the background colour of the photowall
530
		 *
531
		 * @private
532
		 */
533
		_setBackgroundColour: function () {
534
			var wrapper = $('#content-wrapper');
535
			var albumDesign = Gallery.config.albumDesign;
536
			if (!$.isEmptyObject(albumDesign) && albumDesign.background) {
537
				wrapper.css('background-color', albumDesign.background);
538
			} else {
539
				wrapper.css('background-color', '#fff');
540
			}
541
		},
542
543
		/**
544
		 * Picks the image which matches the sort order
545
		 *
546
		 * @param {string} sortType name or date
547
		 * @param {string} sortOrder asc or des
548
		 * @param {boolean} active determines if we're setting up the active sort button
549
		 * @private
550
		 */
551
		_setSortButton: function (sortType, sortOrder, active) {
552
			var button = $('#sort-' + sortType + '-button');
553
			// Removing all the classes which control the image in the button
554
			button.removeClass('active');
555
			button.find('img').removeClass('front');
556
			button.find('img').removeClass('back');
557
558
			// We need to determine the reverse order in order to send that image to the back
559
			var reverseSortOrder = 'des';
560
			if (sortOrder === 'des') {
561
				reverseSortOrder = 'asc';
562
			}
563
564
			// We assign the proper order to the button images
565
			button.find('img.' + sortOrder).addClass('front');
566
			button.find('img.' + reverseSortOrder).addClass('back');
567
568
			// The active button needs a hover action for the flip effect
569
			if (active) {
570
				button.addClass('active');
571
				if (button.is(":hover")) {
572
					button.removeClass('hover');
573
				}
574
				// We can't use a toggle here
575
				button.hover(function () {
576
						$(this).addClass('hover');
577
					},
578
					function () {
579
						$(this).removeClass('hover');
580
					});
581
			}
582
		},
583
584
		/**
585
		 * If no url is entered then do not show the error box.
586
		 *
587
		 */
588
		_blankUrl: function() {
589
			$('#remote_address').on("change keyup paste", function() {
590
 				if ($(this).val() === '') {
591
 					$('#save-button-confirm').prop('disabled', true);
592
 				} else {
593
 					$('#save-button-confirm').prop('disabled', false);
594
 				}
595
			});
596
		},
597
598
		/**
599
		 * Creates the [+] button allowing users who can't drag and drop to upload files
600
		 *
601
		 * @see core/apps/files/js/filelist.js
602
		 * @private
603
		 */
604
		_renderNewButton: function () {
605
			// if no actions container exist, skip
606
			var $actionsContainer = $('.actions.creatable');
607
			if (!$actionsContainer.length) {
608
				return;
609
			}
610
			if (!this._addButtonTemplate) {
611
				this._addButtonTemplate = Handlebars.compile(TEMPLATE_ADDBUTTON);
612
			}
613
			var $newButton = $(this._addButtonTemplate({
614
				addText: t('gallery', 'New'),
615
				iconUrl: OC.imagePath('core', 'actions/add')
616
			}));
617
618
			$actionsContainer.prepend($newButton);
619
			$newButton.tooltip({'placement': 'bottom'});
620
621
			$newButton.click(_.bind(this._onClickNewButton, this));
622
			this._newButton = $newButton;
623
		},
624
625
		/**
626
		 * Creates the click handler for the [+] button
627
		 * @param event
628
		 * @returns {boolean}
629
		 *
630
		 * @see core/apps/files/js/filelist.js
631
		 * @private
632
		 */
633
		_onClickNewButton: function (event) {
634
			var $target = $(event.target);
635
			if (!$target.hasClass('.button')) {
636
				$target = $target.closest('.button');
637
			}
638
			this._newButton.tooltip('hide');
639
			event.preventDefault();
640
			if ($target.hasClass('disabled')) {
641
				return false;
642
			}
643
			if (!this._newFileMenu) {
644
				this._newFileMenu = new Gallery.NewFileMenu();
645
				$('.actions').append(this._newFileMenu.$el);
646
			}
647
			this._newFileMenu.showAt($target);
648
649
			if (Gallery.currentAlbum === '') {
650
				$('.menuitem[data-action="hideAlbum"]').parent().hide();
651
			}
652
			return false;
653
		},
654
655
		/**
656
		 * Creates the download individual files button
657
		 *
658
		 * @see core/apps/files/js/filelist.js
659
		 * @private
660
		 */
661
		_renderDownloadSelectedButton: function () {
662
			if (!this._downloadButtonTemplate) {
663
				this._downloadButtonTemplate = Handlebars.compile(TEMPLATE_DOWNLOADBUTTON);
664
			}
665
			var $downloadButton = $(this._downloadButtonTemplate({
666
				addText: t('gallery', 'Download'),
667
				iconUrl: OC.imagePath('core', 'actions/download')
668
			}));
669
670
			$('#filelist-button').before($downloadButton);
671
			$downloadButton.tooltip({'placement': 'bottom'});
672
673
			$downloadButton.click(_.bind(this._onClickDownloadSelected, this));
674
		},
675
676
		/**
677
		 * Event handler for when clicking on "Download" for the selected files
678
		 *
679
		 * @see core/apps/files/js/filelist.js
680
		 * @private
681
		 */
682
		_onClickDownloadSelected: function(event) {
683
			var files;
684
			var dir = Gallery.currentAlbum;
685
			if (dir === '') {
686
				dir = '/';
687
			}
688
			files = _.pluck(this.getSelectedFiles(), 'path');
689
			files.forEach(function(file, index, files) {
690
				files[index] = OC.basename(file);
691
			});
692
693
			var downloadFileActionIcon = $('#download-selected-button .icon');
694
695
			// don't allow a second click on the download action
696
			if (downloadFileActionIcon.hasClass('icon-loading-small')) {
697
				event.preventDefault();
698
				return;
699
			}
700
701
			var disableLoadingState = function() {
702
				downloadFileActionIcon.removeClass('icon-loading-small');
703
				downloadFileActionIcon.addClass('icon-download');
704
			};
705
			downloadFileActionIcon.removeClass('icon-download');
706
			downloadFileActionIcon.addClass('icon-loading-small');
707
708
			if(this.getSelectedFiles().length > 1) {
709
				OCA.Files.Files.handleDownload(Gallery.getSelectionDownloadUrl(files, dir), disableLoadingState);
710
			}
711
			else {
712
				var first = OC.basename(this.getSelectedFiles()[0].path);
713
				OCA.Files.Files.handleDownload(Gallery.getSelectionDownloadUrl(first, dir), disableLoadingState);
714
			}
715
			return false;
716
	},
717
718
		/**
719
		 * Returns the file info of the selected files
720
		 *
721
		 * @return array of file names
722
		 *
723
		 * @see core/apps/files/js/filelist.js
724
		 * @private
725
		 */
726
		getSelectedFiles: function() {
727
			return _.values(this._selectedFiles);
728
		},
729
730
		/**
731
		 * Returns the file data from a given file element.
732
		 * @param $el file tr element
733
		 * @return file data
734
		 *
735
		 * @see core/apps/files/js/filelist.js
736
		 * @private
737
		 */
738
		elementToFile: function($el){
739
			$el = $($el);
740
			var data = {
741
				id: parseInt($el.attr('data-id'), 10),
742
			};
743
			var path = $el.attr('data-path');
744
			if (path) {
745
				data.path = path;
746
			}
747
			return data;
748
		},
749
750
		/**
751
		 * Selected/deselects the given file element and updated
752
		 * the internal selection cache.
753
		 *
754
		 * @param {Object} $element single image
755
		 * @param {bool} state true to select, false to deselect
756
		 *
757
		 * @see core/apps/files/js/filelist.js
758
		 * @private
759
		 */
760
		_selectFileEl: function($element, state) {
761
			var $checkbox = $element.find('row-element>.image-label>.selectCheckBox');
762
			var oldData = !!this._selectedFiles[$element.data('id')];
763
			var data;
764
			$checkbox.prop('checked', state);
765
			$element.toggleClass('selected', state);
766
			// already selected ?
767
			if (state === oldData) {
768
				return;
769
			}
770
			data = this.elementToFile($element);
771
			if (state) {
772
				this._selectedFiles[$element.data('id')] = data;
773
				this._selectionSummary.add(data);
774
			}
775
			else {
776
				delete this._selectedFiles[$element.data('id')];
777
				this._selectionSummary.remove(data);
778
			}
779
		},
780
781
		/**
782
		 * Event handler for when clicking on a element's checkbox
783
		 *
784
		 * @see core/apps/files/js/filelist.js
785
		 * @private
786
		 */
787
		_onClickFileCheckbox: function(event) {
788
			var $image = $(event.target).closest('.' + GalleryImage.cssClass);
789
			var state = !$image.hasClass('selected');
790
			this._selectFileEl($image, state);
791
			this._lastChecked = $image;
792
			this.updateSelectionSummary();
793
		},
794
795
		/**
796
		 * Update UI based on the current selection
797
		 *
798
		 * @see core/apps/files/js/filelist.js
799
		 * @private
800
		 */
801
		updateSelectionSummary: function() {
802
			var summary = this._selectionSummary.summary;
803
804
			if (summary.totalFiles === 0 && summary.totalDirs === 0) {
805
				$('#download-selected-button').addClass('hidden');
806
			}
807
			else {
808
				$('#download-selected-button').removeClass('hidden');
809
			}
810
		},
811
812
	};
813
814
	Gallery.View = View;
815
})(jQuery, _, OC, t, Gallery);
816